package com.ck.company.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ck.chuangke.service.CkCompanyService;
import com.ck.common.service.OperationLogService;
import com.ck.company.service.*;
import com.ck.config.CustomProperties;
import com.ck.constant.*;
import com.ck.dao.*;
import com.ck.exception.ServiceException;
import com.ck.model.chuangke.vo.CkProgramListVO;
import com.ck.model.common.*;
import com.ck.model.common.sys.LoginUser;
import com.ck.model.company.bo.CpProgramZipBO;
import com.ck.model.company.bo.CpSceneSourceZipBO;
import com.ck.model.company.dto.CpProgramDTO;
import com.ck.model.company.dto.CpProgramPutinDTO;
import com.ck.model.company.vo.*;
import com.ck.util.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

/**
 * 商家节目
 *
 * @author linqiang
 * @date 2018-06-20 18:30
 */
@Service(value = "cpProgramService")
public class CpProgramServiceImpl implements CpProgramService {
    private Logger logger = LoggerFactory.getLogger(CpProgramServiceImpl.class);

    @Autowired
    private ProgramMapper programMapper;
    @Autowired
    private ProgramDownloadMapper programDownloadMapper;
    @Autowired
    private DeviceProgramMapper deviceProgramMapper;
    @Autowired
    private ProgramPutinMapper programPutinMapper;
    @Autowired
    private SceneTopMapper sceneTopMapper;
    @Autowired
    private SceneMiddleMapper sceneMiddleMapper;
    @Autowired
    private SceneBottomMapper sceneBottomMapper;
    @Autowired
    private SceneMapper sceneMapper;
    @Autowired
    private OperationLogService operationLogService;
    @Autowired
    private CpDeviceInfoService cpDeviceInfoService;
    @Autowired
    private CustomProperties customProperties;
    @Resource()
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private SpringContextUtil springContextUtil;
    @Autowired
    private JsonDataMapper jsonDataMapper;
    @Autowired
    private CpProgramLogService cpProgramLogService;
    @Autowired
    private ProgramTemplateMapper programTemplateMapper;
    @Autowired
    private ProgramParseService programParseService;
    @Autowired
    private SceneSourceMapper sceneSourceMapper;
    @Autowired
    private CpScreenCanvasMappingService cpScreenCanvasMappingService;
    @Autowired
    private CpJsonDataService cpJsonDataService;
    @Autowired
    private CkCompanyService ckCompanyService;


    /**
     * 节目列表分页
     *
     * @param companyCode   商家编号
     * @param programName   节目名称
     * @param programStatus 节目状态
     * @param pageNum       当前页
     * @param pageSize      每页条数
     * @return page
     */
    @Override
    public Page<CpProgramListVO> listByPage(String companyCode, String programName, Integer programStatus,
                                            Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return programMapper.listByPageCp(companyCode, programName, programStatus);
    }

    /**
     * 节目终端列表分页
     *
     * @param companyCode    商家编号
     * @param programVersion 节目版本号
     * @param deviceCode     终端机器码
     * @param pageNum        当前页
     * @param pageSize       每页条数
     * @return page
     */
    @Override
    public Page<CpProgramDeviceListVO> listProgramDeviceByPage(String companyCode, String programVersion, String deviceCode,
                                                               Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return programMapper.listProgramDeviceByPage(companyCode, programVersion, deviceCode);
    }

    /**
     * 获取节目终端统计信息
     *
     * @param companyCode    商家编号
     * @param programVersion 节目版本号
     * @param deviceCode     终端序列号
     * @return CpProgramDeviceCountVO
     */
    @Override
    public CpProgramDeviceCountVO getProgramDeviceCount(String companyCode, String programVersion, String deviceCode) {
        CpProgramDeviceCountVO cp = programMapper.getProgramDeviceCount(companyCode, programVersion, deviceCode);
        if (null != cp) {
            int notBeginCount = cp.getTotalCount()
                    - cp.getDownloadingCount()
                    - cp.getFailCount()
                    - cp.getSuccessCount();
            cp.setNotBeginCount(notBeginCount);
        }
        return cp;
    }

    /**
     * 修改节目状态
     *
     * @param programId     节目主键
     * @param programStatus 节目状态
     * @param request       请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProgramStatus(Long programId, Integer programStatus, Long updateTime, HttpServletRequest request)
            throws ServiceException {
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            throw new ServiceException("节目不存在或已被删除");
        }
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        String optMsg = "";
        if (ProgramConstant.PROGRAM_STATUS_APPROVED.equals(programStatus)) {
            //压缩节目包到华为云，并返回存储地址
            taskExecutor.execute(() -> {
                String programZipPath = null;
                try {
                    programZipPath = createProgramZip(programId, request);
                    if (StringUtils.isNotBlank(programZipPath)) {
                        Program program1 = programMapper.selectByPrimaryKey(programId);
                        program1.setZipUrl(programZipPath);
                        program1.setUpdateTime(System.currentTimeMillis());
                        programMapper.updateByPrimaryKey(program1);
                        flushCache(program.getProgramVersion(), user.getCompanyCode(), updateTime);
                    }
                } catch (IOException | ServiceException e) {
                    e.printStackTrace();
                    logger.error("审批节目时压缩zip包失败，节目ID：" + program.getProgramId() + ", 失败原因 ：" + e.getMessage());
                    programMapper.notApprove(programId, System.currentTimeMillis(), "系统后台压缩zip失败", user.getCompanyCode());
                }
            });
            optMsg = "审批节目成功";
            cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_APPROVE_PROGRAM, user);
        } else if (ProgramConstant.PROGRAM_STATUS_APPROVAL_PENDING.equals(programStatus)) {
            optMsg = "发布节目成功";
            cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_PUBLISH_PROGRAM, user);
        } else {
            optMsg = "操作成功";
        }
        int row = programMapper.updateProgramStatus(programId, programStatus, updateTime, user.getCompanyCode());
        operationLogService.saveSuccessOperationLog(optMsg + "，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
        return row;
    }

    /**
     * 不通过节目
     *
     * @param programId  节目ID
     * @param updateTime 更新时间
     * @param reason     不通过原因
     * @param request    请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int notApprove(Long programId, Long updateTime, String reason,
                          HttpServletRequest request) throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        int row = programMapper.notApprove(programId, updateTime, reason, user.getCompanyCode());
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_APPROVE_PROGRAM, user);
        operationLogService.saveSuccessOperationLog("驳回节目成功，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
        return row;
    }

    /**
     * 通过节目
     *
     * @param programId 节目ID
     * @param request   请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int approve(Long programId, HttpServletRequest request) throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        int row = programMapper.approve(programId, System.currentTimeMillis(), user.getCompanyCode());
        if (row > 0) {
            try {
                //压缩节目包到华为云，并返回存储地址
                taskExecutor.execute(() -> {
                    String programZipPath = null;
                    try {
                        programZipPath = createProgramZip(programId, request);
                        if (StringUtils.isNotBlank(programZipPath)) {
                            //压缩成功
                            Program program1 = programMapper.selectByPrimaryKey(programId);
                            program1.setZipUrl(programZipPath);
                            program1.setUpdateTime(System.currentTimeMillis());
                            programMapper.updateByPrimaryKey(program1);
                            flushCache(program.getProgramVersion(), user.getCompanyCode(), System.currentTimeMillis());
                        } else {
                            //压缩失败
                            programMapper.notApprove(programId, System.currentTimeMillis(), "压缩节目包失败，请检查节目信息是否正确", user.getCompanyCode());
                        }
                    } catch (IOException | ServiceException e) {
                        e.printStackTrace();
                        logger.error("审批节目时压缩zip包失败，节目ID：" + program.getProgramId() + ", 失败原因 ：" + e.getMessage());
                        programMapper.notApprove(programId, System.currentTimeMillis(), "压缩节目包失败", user.getCompanyCode());
                    }
                });
                operationLogService.saveSuccessOperationLog("通过节目成功，节目ID为：" + program.getProgramId()
                        + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException(ProgramConstant.MSG_PROGRAM_ZIP_COMPRESS_FAIL);
            }
        } else {
            operationLogService.saveSuccessOperationLog("通过节目失败，节目ID为：" + program.getProgramId()
                    + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
        }
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_APPROVE_PROGRAM, user);
        return row;
    }

    /**
     * 根据主键删除单个节目
     *
     * @param programId 节目主键
     * @param request   请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteByPrimaryKey(Long programId, HttpServletRequest request) throws ServiceException {
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        flushCache(program.getProgramVersion(), user.getCompanyCode(), System.currentTimeMillis());
        if (null != program.getJsonId()) {
            cpJsonDataService.deleteByPrimaryKey(program.getJsonId());
        }
        int row = programMapper.deleteByPrimaryKey(programId, user.getCompanyCode());
        operationLogService.saveSuccessOperationLog("删除节目成功，节目ID为："
                        + program.getProgramId() + "，节目名称为：" + program.getProgramName(),
                ModuleConstant.MODULE_PROGRAM, request);
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_DELETE_PROGRAM, user);
        return row;
    }

    /**
     * 根据主键查询单个节目
     *
     * @param programId 节目主键
     * @return Program
     */
    @Override
    public Program selectByPrimaryKey(Long programId) {
        return programMapper.selectByPrimaryKey(programId);
    }

    /**
     * 批量删除节目的终端
     *
     * @param programVersion 节目版本号
     * @param deviceCodes    终端code（多个以英文逗号隔开）
     * @param request        请求
     * @return 成功返回true, 反之false
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean batchDeleteDeviceFromProgram(String programVersion, String deviceCodes, HttpServletRequest request)
            throws ServiceException {
        Program program = programMapper.selectByProgramVersion(programVersion);
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        String[] deviceCodeArr = deviceCodes.split(",");
        String[] programVersionArr = new String[]{programVersion};
        if (deviceCodeArr.length > 0) {
            Long now = System.currentTimeMillis();
            cpDeviceInfoService.flushDeviceStateCache(user.getCompanyCode(), DeviceSettingType.PROGRAM_UPDATE,
                    Arrays.asList(deviceCodeArr), now);
            // 逻辑删除节目终端关联
            deviceProgramMapper.batchDeleteDeviceFromProgram(programVersion, deviceCodeArr, now);
            // 逻辑删除节目下载关联
            programDownloadMapper.batchDeleteDeviceFromProgram(programVersion, deviceCodeArr);
            //批量减去节目终端总数
            programMapper.batchMinusTotalCount(programVersionArr, deviceCodeArr.length);
            // 统计节目终端数量
            int deviceProgramCount = programMapper.countDeviceProgramByProgramVersion(programVersion);
            if (deviceProgramCount == 0) {
                //删除投放设置
                programPutinMapper.batchDeleteByProgramVersion(programVersion);
                if (null != program.getJsonId()) {
                    cpJsonDataService.deleteByPrimaryKey(program.getJsonId());
                }
                //无终端播放该节目，将节目状态改为‘已过期’
                programMapper.updateProgramToExpired(programVersion, now, now, user.getCompanyCode());
            }
            cpProgramLogService.saveSuccessOperationLog(programVersion, ProgramLogConstant.OPT_DELETE_PROGRAM_FROM_DEVICE, user);
            operationLogService.saveSuccessOperationLog("批量删除节目的终端成功，节目ID为："
                    + program.getProgramId() + "，节目名称为：" + program.getProgramName()
                    + "，终端CODE为：" + deviceCodes, ModuleConstant.MODULE_PROGRAM, request);
            return true;
        }
        return false;
    }

    /**
     * 批量从终端删除节目
     *
     * @param programVersions 节目版本号(多个以英文逗号隔开)
     * @param deviceCode      终端code
     * @param request         请求
     * @return boolean
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean batchDeleteProgramFromDevice(String programVersions, String deviceCode, HttpServletRequest request)
            throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        String[] programVersionArr = programVersions.split(",");
        if (programVersionArr.length > 0) {
            Long now = System.currentTimeMillis();
            //刷新终端的节目缓存信息
            List<String> codeList = new ArrayList<>();
            codeList.add(deviceCode);
            cpDeviceInfoService.flushDeviceStateCache(user.getCompanyCode(), DeviceSettingType.PROGRAM_UPDATE, codeList, now);
            deviceProgramMapper.batchDeleteProgramFromDevice(programVersionArr, deviceCode);
            programDownloadMapper.batchDeleteProgramFromDevice(programVersionArr, deviceCode);
            //批量减去节目终端总数
            programMapper.batchMinusTotalCount(programVersionArr, 1);
            //新增节目操作日志
            cpProgramLogService.batchSaveSuccessOperationLog(programVersionArr, ProgramLogConstant.OPT_DELETE_PROGRAM_FROM_DEVICE, user);
            for (String programVersion : programVersionArr) {
                // 统计节目终端数量
                int deviceProgramCount = programMapper.countDeviceProgramByProgramVersion(programVersion);
                if (deviceProgramCount == 0) {
                    //删除投放设置
                    programPutinMapper.batchDeleteByProgramVersion(programVersion);
                    Program program = programMapper.selectByProgramVersion(programVersion);
                    if (null != program && null != program.getJsonId()) {
                        cpJsonDataService.deleteByPrimaryKey(program.getJsonId());
                    }
                    //无终端播放该节目，将节目状态改为‘已过期’
                    programMapper.updateProgramToExpired(programVersion, now, now, user.getCompanyCode());
                }
            }
            String programInfo = programMapper.selectProgramIdAndNameByVersions(programVersionArr);
            String content = "批量从终端删除节目成功，终端CODE为：" + deviceCode;
            if (StringUtils.isNotBlank(programInfo)) {
                String[] infoArr = programInfo.split(";");
                int length = 2;
                if (infoArr.length == length) {
                    String ids = infoArr[0];
                    String names = infoArr[1];
                    content += ",节目ID为：" + ids + "，节目名称为：" + names;
                }
            }
            operationLogService.saveSuccessOperationLog(content, ModuleConstant.MODULE_PROGRAM, request);
            return true;
        }
        return false;
    }

    /**
     * 根据节目版本号删除节目
     *
     * @param programVersion 节目版本号
     * @param request        请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteByProgramVersion(String programVersion, HttpServletRequest request) throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        Program program = programMapper.selectByProgramVersion(programVersion);
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        int row = 0;
        flushCache(programVersion, user.getCompanyCode(), System.currentTimeMillis());
        //删除节目下载表相数据（逻辑删除）
        programDownloadMapper.deleteByProgramVersion(programVersion, user.getCompanyCode());
        //删除节目终端表相关数据（逻辑删除）
        deviceProgramMapper.deleteByProgramVersion(programVersion, System.currentTimeMillis());
        if (null != program.getJsonId()) {
            cpJsonDataService.deleteByPrimaryKey(program.getJsonId());
        }
        row = programMapper.deleteByProgramVersionWithDataStatus(programVersion, user.getCompanyCode());
        operationLogService.saveSuccessOperationLog(("删除节目成功，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName()), ModuleConstant.MODULE_PROGRAM, request);
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_DELETE_PROGRAM, user);
        return row;
    }

    /**
     * 修改节目添加状态
     *
     * @param programId  节目ID
     * @param addStatus  添加状态
     * @param updateTime 修改时间
     * @param request    请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProgramAddStatus(Long programId, Integer addStatus, Long updateTime, HttpServletRequest request)
            throws ServiceException {
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        if (addStatus.equals(program.getAddStatus())) {
            return 1;
        }
        int row = programMapper.updateProgramAddStatus(programId, addStatus, updateTime, user.getCompanyCode());
        operationLogService.saveSuccessOperationLog("修改节目内容成功，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
        return row;
    }

    /**
     * 创建节目
     *
     * @param cpProgramDTO 节目DTO对象
     * @param request      请求
     * @return 成功返回空字符，失败返回失败原因
     */
    @Deprecated
    @Transactional(rollbackFor = Exception.class)
    @Override
    public long saveProgram(CpProgramDTO cpProgramDTO, HttpServletRequest request) throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        long dateNow = System.currentTimeMillis();
        String programVersion = UUIDUtils.getUUIDString();
        Program program = new Program();
        program.setDataStatus(ProgramConstant.DATA_STATUS_EFFECTIVE);
        program.setAddStatus(ProgramConstant.ADD_STATUS_APPROVAL_SET_LAUNCH);
        program.setProgramName(cpProgramDTO.getProgramName());
        program.setProgramVersion(programVersion);
        program.setProgramStatus(ProgramConstant.PROGRAM_STATUS_PUBLISH_PENDING);
        program.setAddTime(String.valueOf(dateNow));
        program.setAddUserName(user.getUserName());
        program.setCompanyCode(user.getCompanyCode());
        programMapper.insert(program);
        operationLogService.saveSuccessOperationLog("创建节目成功，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
        return program.getProgramId();
    }

    /**
     * 修改节目
     *
     * @param cpProgramDTO 节目数据对象
     * @param request      请求
     * @return 成功返回空字符，失败返回失败原因
     */
    @Deprecated
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String updateProgram(CpProgramDTO cpProgramDTO, HttpServletRequest request) throws ServiceException {
        Program program = programMapper.selectByPrimaryKey(cpProgramDTO.getProgramId());
        if (null == program) {
            throw new ServiceException(ProgramConstant.MSG_PROGRAM_NOT_EXITS);
        }
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        //清除节目已有的场景
        sceneTopMapper.batchDeleteByProgramVersion(program.getProgramVersion());
        sceneMiddleMapper.batchDeleteByProgramVersion(program.getProgramVersion());
        sceneBottomMapper.batchDeleteByProgramVersion(program.getProgramVersion());
        sceneMapper.batchDeleteByProgramVersion(program.getProgramVersion());
        program.setProgramName(cpProgramDTO.getProgramName());
        operationLogService.saveSuccessOperationLog("修改节目成功，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, request);
        return "";
    }


    /**
     * 导出全部节目列表
     *
     * @param programStatus
     * @param companyCode
     * @param programName
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public List<CpAllProgramExportVO> allProgramListExport(Integer programStatus, String companyCode, String programName, Long startTime, Long endTime) {
        return programMapper.allProgramListCp(programStatus, companyCode, programName, startTime, endTime);
    }

    /**
     * 全部节目列表
     *
     * @param programStatus
     * @param companyCode
     * @param programName
     * @param startTime
     * @param endTime
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public Page<CkProgramListVO> allProgramList(Integer programStatus, String companyCode, String programName, Long startTime, Long endTime, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return programMapper.allProgramPageCp(programStatus, companyCode, programName, startTime, endTime);
    }

    /**
     * 根据节目主键查询节目详情DTO
     *
     * @param programId 节目主键
     * @param request   请求
     * @return CpProgramDTO
     */
    @Override
    public CpProgramDTO selectByPrimaryKeyDTO(Long programId, HttpServletRequest request) throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            return null;
        }
        CpProgramDTO cpProgramDTO = new CpProgramDTO();
        cpProgramDTO.setProgramId(program.getProgramId());
        cpProgramDTO.setProgramName(program.getProgramName());
        cpProgramDTO.setAddStatus(program.getAddStatus());
        cpProgramDTO.setProgramStatus(program.getProgramStatus());
        cpProgramDTO.setNoPassReason(program.getNoPassReson());
        cpProgramDTO.setJsonId(program.getJsonId());
        cpProgramDTO.setProgramTemplateId(program.getProgramTemplateId());

        //商家图标
        String companyCode = user.getCompanyCode();
        if (StringUtils.isNotBlank(companyCode)) {
            Company company = ckCompanyService.getCompanyInfoByCompanyCode(companyCode);
            if (null != company) {
                cpProgramDTO.setCompanyLogoUrl(StringUtils.isBlank(company.getCompanyLogoUrl()) ? "" : company.getCompanyLogoUrl());
            }
        }

        //节目编辑页上一步，下一步地址
        String baseUrl = customProperties.getProgramEditServerDomain();
        String programEditClientBackUri = customProperties.getProgramEditClientBackUri();
        String programEditClientNextUri = customProperties.getProgramEditClientNextUri();
        cpProgramDTO.setBackUrl(baseUrl + programEditClientBackUri);
        cpProgramDTO.setNextUrl(baseUrl + programEditClientNextUri + "?programId=" + programId);

        //模板类型
        ProgramTemplate programTemplate = programTemplateMapper.selectByPrimaryKey(program.getProgramTemplateId());
        if (null != programTemplate) {
            cpProgramDTO.setProgramTemplateType(programTemplate.getType());
        }

        JSONObject jsonObject = null;
        if (null != program.getJsonId()) {
            JsonData jsonData = jsonDataMapper.selectByPrimaryKey(program.getJsonId());
            if (null != jsonData) {
                String jsonContent = jsonData.getJsonContent();
                jsonObject = JSONObject.parseObject(jsonContent);
            } else {
                jsonObject = new JSONObject();
            }
        } else {
            jsonObject = new JSONObject();
        }

        //场景
        List<Scene> sceneList = sceneMapper.listByProgramVersion(program.getProgramVersion());
        cpProgramDTO.setScenes(sceneList);

        //投放设置
        CpProgramPutinDTO cpProgramPutinDTO = programPutinMapper.selectByProgramVersionDTO(program.getProgramVersion());
        if (null != cpProgramPutinDTO) {
            jsonObject.put("putinStartTime", cpProgramPutinDTO.getPutinStartTime());
            jsonObject.put("putinEndTime", cpProgramPutinDTO.getPutinEndTime());
            //拼接投放时间段
            String startTime = cpProgramPutinDTO.getStartTime();
            String endTime = cpProgramPutinDTO.getEndTime();
            if (ProgramPutinConstant.UNLIMIT_START_TIME.equals(cpProgramPutinDTO.getPutinStartTime())) {
                startTime = ProgramPutinConstant.UNLIMIT_STR;
                cpProgramPutinDTO.setStartTime("");
            }
            if (ProgramPutinConstant.UNLIMIT_END_TIME.equals(cpProgramPutinDTO.getPutinEndTime())) {
                endTime = ProgramPutinConstant.UNLIMIT_STR;
                cpProgramPutinDTO.setEndTime("");
            }
            cpProgramPutinDTO.setDateRange(startTime + " - " + endTime);
            if (StringUtils.isNotBlank(cpProgramPutinDTO.getPutinDeviceGroup())) {
                String groupName = "";
                String groupIdStr = cpProgramPutinDTO.getPutinDeviceGroup();
                List<String> groupIdList = new ArrayList<>(Arrays.asList(groupIdStr.split(",")));
                //包含未分组
                if (groupIdList.contains(String.valueOf(DeviceGroupConstant.NO_DEVICE_GROUP_ID))) {
                    groupName += "," + DeviceGroupConstant.NO_DEVICE_GROUP_NAME;
                    groupIdList.remove(String.valueOf(DeviceGroupConstant.NO_DEVICE_GROUP_ID));
                }
                //包含全部分组
                if (groupIdList.contains(String.valueOf(DeviceGroupConstant.ALL_DEVICE_GROUP_ID))) {
                    groupName += "," + DeviceGroupConstant.ALL_DEVICE_GROUP_NAME;
                    groupIdList.remove(String.valueOf(DeviceGroupConstant.ALL_DEVICE_GROUP_ID));
                }
                if (groupIdList.size() > 0) {
                    String deviceGroupName = programPutinMapper.selectDeviceGroupNameByDeviceGroupCode(groupIdList, user.getCompanyCode());
                    if (StringUtils.isNotBlank(deviceGroupName)) {
                        groupName += "," + deviceGroupName;
                    }
                }
                if (StringUtils.isNotBlank(groupName)) {
                    groupName = groupName.substring(1);
                }
                cpProgramPutinDTO.setPutinDeviceGroupName(groupName);
            }
        }
        cpProgramDTO.setJson(JSON.toJSONString(jsonObject));
        cpProgramDTO.setServingInfo(cpProgramPutinDTO == null ? new CpProgramPutinDTO() : cpProgramPutinDTO);
        return cpProgramDTO;
    }

    /**
     * 单个节目分页列表
     *
     * @param companyCode 商家代码
     * @param programName 节目名称
     * @param programId   节目ID
     * @param pageNum     当前页数
     * @param pageSize    每页条数
     * @return
     */
    @Override
    public Page<CpSingleProgramVO> singleProgramList(String companyCode, String programName, String programId, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null ? PageConstant.DEFAULT_PAGE_NUM : pageNum);
        pageSize = (pageSize == null ? PageConstant.DEFAULT_PAGE_SIZE : pageSize);
        PageHelper.startPage(pageNum, pageSize);
        return programMapper.singleProgramListCp(companyCode, programName, programId);
    }

    @Override
    public List<CpSingleProgramVO> singleProgramListExport(String companyCode, String programId, String programName) {
        return programMapper.singleProgramListCp(companyCode, programName, programId);
    }

    /**
     * 根据数据状态和节目状态查询节目集合
     *
     * @param dataStatus    数据状态（0正常，1逻辑删除）
     * @param programStatus 节目状态(0：已审批，1：待审批，2：待发布，3：已过期)
     * @return List<Program>
     */
    @Override
    public List<Program> list(Integer dataStatus, Integer programStatus) {
        return programMapper.list(dataStatus, programStatus);
    }

    /**
     * 批量更新节目为已过期
     *
     * @param versionList 更新节目版本号集合
     * @param updateTime  更新时间
     * @param request     请求
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int batchUpdateProgramToExpired(List<String> versionList, Long updateTime, HttpServletRequest request)
            throws ServiceException {
        User user = (User) request.getSession().getAttribute(RequestConstant.SESSION_KEY_USER);
        if (null == user) {
            throw new ServiceException(UserConstant.USER_NOT_LOGIN);
        }
        int row = 0;
        if (CollectionUtils.isNotEmpty(versionList)) {
            for (String version : versionList) {
                flushCache(version, user.getCompanyCode(), updateTime);
            }
        }
        row = programMapper.batchUpdateProgramToExpired(versionList, updateTime, updateTime, user.getCompanyCode());
        operationLogService.saveSuccessOperationLog("批量更新节目为已过期，节目版本号为："
                + versionList, ModuleConstant.MODULE_PROGRAM, request);
        return row;
    }


    /**
     * 批量更新节目为已过期(系统内部自动检测)
     *
     * @param versionList 更新节目版本号集合
     * @param updateTime  更新时间
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int batchUpdateProgramToExpiredLocal(List<String> versionList, Long updateTime) {
        int row = 0;
        if (CollectionUtils.isNotEmpty(versionList)) {
            row = programMapper.batchUpdateProgramToExpired(versionList, updateTime, updateTime, null);
            OperationLog operationLog = new OperationLog();
            operationLog.setOperationContent("批量更新节目为已过期");
            operationLog.setOperationStatus(BusinessConstant.OPERATION_LONG_OPERATION_STATUS_SUCCESS);
            operationLog.setOperationTime(System.currentTimeMillis());
            operationLog.setOperationModule(ModuleConstant.MODULE_PROGRAM);
            operationLogService.saveOperationLog(operationLog);
        }
        return row;
    }


    /**
     * 查询节目数量（programStatus为null,查询节目总数）
     *
     * @param companyCode   商家编码
     * @param programStatus 节目状态
     * @return Long
     */
    @Override
    public Long countProgram(String companyCode, Integer programStatus) throws ServiceException {
        if (StringUtils.isBlank(companyCode)) {
            throw new ServiceException("商家编码不能为空");
        }
        return programMapper.countProgram(companyCode, programStatus);
    }

    /**
     * 清除节目不通过原因
     *
     * @param companyCode 商家编号
     * @param programId   节目ID
     * @return 影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int cleanProgramNoPassReason(String companyCode, Long programId) {
        return programMapper.cleanProgramNoPassReason(companyCode, programId);
    }

    /**
     * 新增节目（空节目）
     *
     * @param programName       节目名称
     * @param programResolution 节目尺寸
     * @param programTemplateId 模板ID
     * @param loginUser         登录用户信息
     * @return 新增的节目ID
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public long saveProgram(String programName, String programResolution, Long programTemplateId, LoginUser loginUser) {
        //检查重复节目名称
        int countByProgramName = programMapper.countByProgramName(programName, loginUser.getUser().getCompanyCode());
        if (countByProgramName > 0) {
            return 0;
        }
        Program program = new Program();

        String[] resolutionArr = programResolution.split("\\*");
        Integer wide = Integer.valueOf(resolutionArr[0]);
        Integer height = Integer.valueOf(resolutionArr[1]);

        long now = System.currentTimeMillis();
        JsonData jsonData = new JsonData();
        jsonData.setAddTime(now);
        jsonData.setType(JsonDataTypeEnum.PROGRAM_JSON.getType());
        if (null != programTemplateId) {
            ProgramTemplate programTemplate = programTemplateMapper.selectByPrimaryKey(programTemplateId);
            if (null != programTemplate && null != programTemplate.getJsonId()) {
                JsonData jsonDataTemp = jsonDataMapper.selectByPrimaryKey(programTemplate.getJsonId());
                if (null != jsonDataTemp) {
                    jsonData.setJsonContent(jsonDataTemp.getJsonContent());
                }
                if (null != programTemplate.getType()
                        && ProgramTemplateTypeEnum.USER_TEMP.getType().equals(programTemplate.getType())) {
                    program.setProgramTemplateId(programTemplateId);
                }
            }
        }
        jsonDataMapper.insert(jsonData);
        program.setJsonId(jsonData.getJsonId());

        //画布宽高
        JsonData jsonDataProgram = jsonDataMapper.selectByPrimaryKey(program.getJsonId());
        ScreenCanvasMapping screenCanvasMapping = cpScreenCanvasMappingService.selectByScreen(wide, height);
        Integer canvasWidth = screenCanvasMapping.getCanvasWidth();
        Integer canvasHeight = screenCanvasMapping.getCanvasHeight();
        JSONObject jsonObject = null;
        if (StringUtils.isBlank(jsonDataProgram.getJsonContent())) {
            jsonObject = new JSONObject();
        } else {
            jsonObject = JSONObject.parseObject(jsonDataProgram.getJsonContent());
        }
        jsonObject.put("canvasWidth", canvasWidth);
        jsonObject.put("canvasHeight", canvasHeight);
        jsonObject.put("screenWidth", wide);
        jsonObject.put("screenHeight", height);
        String jsonContent = jsonObject.toJSONString();
        jsonDataProgram.setJsonContent(jsonContent);
        jsonDataMapper.updateByPrimaryKey(jsonDataProgram);

        long dateNow = System.currentTimeMillis();
        String programVersion = UUIDUtils.getUUIDString();
        program.setDataStatus(ProgramConstant.DATA_STATUS_EFFECTIVE);
        program.setAddStatus(ProgramConstant.ADD_STATUS_APPROVAL_UPLOAD_MATERIAL);
        program.setProgramVersion(programVersion);
        program.setProgramStatus(ProgramConstant.PROGRAM_STATUS_PUBLISH_PENDING);
        program.setAddTime(String.valueOf(dateNow));
        program.setUpdateTime(dateNow);
        program.setAddUserName(null == loginUser.getUser() ? "" : loginUser.getUser().getUserName());
        program.setCompanyCode(null == loginUser.getUser() ? "" : loginUser.getUser().getCompanyCode());
        program.setProgramName(programName);
        program.setWide(wide);
        program.setHeight(height);
        program.setOverdueTime(DateUtils.getPreMonth(dateNow));
        programMapper.insert(program);
        operationLogService.saveSuccessOperationLog("创建节目成功，节目ID为：" + program.getProgramId()
                + "，节目名称为：" + program.getProgramName(), ModuleConstant.MODULE_PROGRAM, loginUser.getUser(), loginUser.getIp());
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_ADD_PROGRAM, loginUser.getUser());
        return program.getProgramId();
    }

    /**
     * 编辑节目
     *
     * @param programId         节目ID
     * @param programTemplateId 节目
     * @param json              节目数据json
     * @param loginUser         登录用户信息
     * @return 编辑结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String updateProgram(Long programId, Long programTemplateId, String json, LoginUser loginUser) {
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            return ProgramConstant.MSG_PROGRAM_NOT_EXITS;
        }
        long now = System.currentTimeMillis();

        //解析节目json
        Map<String, Object> programDataMap = programParseService.getProgramDataFromJson(json);
        //节目大小
        Long programSize = (Long) programDataMap.get("programSize");
        //节目时长
        Integer programDuration = (Integer) programDataMap.get("programDuration");
        //场景
        List<Scene> scenes = (List<Scene>) programDataMap.get("sceneList");
        if (CollectionUtils.isNotEmpty(scenes)) {
            for (Scene scene : scenes) {
                scene.setProgramVersion(program.getProgramVersion());
            }
        }
        //素材
        List<SceneSource> sceneSourceList = (List<SceneSource>) programDataMap.get("sceneSourceList");

        //屏幕分辨率宽高
        Integer screenWidth = programDataMap.get("screenWidth") == null ? 0 : (Integer) programDataMap.get("screenWidth");
        Integer screenHeight = programDataMap.get("screenHeight") == null ? 0 : (Integer) programDataMap.get("screenHeight");

        //画布宽高
        Integer canvasWidth = programDataMap.get("canvasWidth") == null ? 0 : (Integer) programDataMap.get("canvasWidth");
        Integer canvasHeight = programDataMap.get("canvasHeight") == null ? 0 : (Integer) programDataMap.get("canvasHeight");
        JSONObject jsonObject = JSONObject.parseObject(json);
        jsonObject.put("canvasWidth", canvasWidth);
        jsonObject.put("canvasHeight", canvasHeight);
        jsonObject.put("screenWidth", screenWidth);
        jsonObject.put("screenHeight", screenHeight);
        json = jsonObject.toJSONString();

        //清空节目原有场景和素材
        sceneMapper.batchDeleteByProgramVersion(program.getProgramVersion());
        sceneSourceMapper.batchDeleteByProgramVersion(program.getProgramVersion());

        //批量插入场景，素材
        if (CollectionUtils.isNotEmpty(scenes)) {
            sceneMapper.batchInsert(scenes);
        }
        if (CollectionUtils.isNotEmpty(sceneSourceList)) {
            sceneSourceMapper.batchInsert(sceneSourceList);
        }

        JsonData jsonData = null;
        //节目json
        if (null == program.getJsonId()) {
            jsonData = new JsonData();
            jsonData.setAddTime(now);
            jsonData.setJsonContent(json);
            jsonData.setType(JsonDataTypeEnum.PROGRAM_JSON.getType());
            jsonDataMapper.insert(jsonData);
            program.setJsonId(jsonData.getJsonId());
        } else {
            jsonData = jsonDataMapper.selectByPrimaryKey(program.getJsonId());
            if (jsonData != null) {
                jsonData.setJsonContent(json);
                jsonData.setUpdateTime(now);
                jsonDataMapper.updateByPrimaryKey(jsonData);
            } else {
                jsonData = new JsonData();
                jsonData.setAddTime(now);
                jsonData.setJsonContent(json);
                jsonData.setType(JsonDataTypeEnum.PROGRAM_JSON.getType());
                jsonDataMapper.insert(jsonData);
                program.setJsonId(jsonData.getJsonId());
                //删除旧的jsonData数据
                jsonDataMapper.deleteByPrimaryKey(program.getJsonId());
            }
        }

        program.setUpdateTime(now);
        program.setProgramSize(programSize);
        program.setWide(screenWidth);
        program.setHeight(screenHeight);
        program.setSceneNum(scenes.size());
        program.setProgramDuration(programDuration);
        program.setOverdueTime(DateUtils.getPreMonth(now));
        program.setProgramTemplateId(programTemplateId);
        programMapper.updateByPrimaryKey(program);
        operationLogService.saveSuccessOperationLog(
                ("编辑节目成功，节目名称为：" + program.getProgramName() + "，节目版本号为：" + program.getProgramVersion()),
                ModuleConstant.MODULE_PROGRAM, loginUser.getUser(), loginUser.getIp());
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_EDIT_PROGRAM, loginUser.getUser());
        return "";
    }


    /**
     * 编辑节目(保存并跳转到投放设置,需要验证节目是否符合要求)
     *
     * @param programId         节目ID
     * @param programTemplateId 节目
     * @param json              节目数据json
     * @param loginUser         登录用户信息
     * @return 编辑结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String updateProgramAndJump(Long programId, Long programTemplateId, String json, LoginUser loginUser) {
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            return ProgramConstant.MSG_PROGRAM_NOT_EXITS;
        }
        long now = System.currentTimeMillis();

        //解析节目json
        Map<String, Object> programDataMap = programParseService.getProgramDataFromJson(json);

        //节目大小
        Long programSize = (Long) programDataMap.get("programSize");
        if (programSize == 0) {
            return ProgramConstant.MSG_PROGRAM_SIZE_NOT_NULL;
        }
        if (programSize > customProperties.getMaxProgramSize()) {
            return "节目不能超过" + (customProperties.getMaxProgramSize() / 1024 / 1024) + "M";
        }
        //节目时长
        Integer programDuration = (Integer) programDataMap.get("programDuration");
        if (programDuration == 0) {
            return ProgramConstant.MSG_PROGRAM_DURATION_NOT_ZERO;
        }

        //场景
        List<Scene> scenes = (List<Scene>) programDataMap.get("sceneList");
        if (CollectionUtils.isEmpty(scenes)) {
            return ProgramConstant.MSG_PROGRAM_SCENE_NOT_NULL;
        } else {
            for (Scene scene : scenes) {
                scene.setProgramVersion(program.getProgramVersion());
            }
        }

        //素材
        List<SceneSource> sceneSourceList = (List<SceneSource>) programDataMap.get("sceneSourceList");
        if (CollectionUtils.isEmpty(sceneSourceList)) {
            return ProgramConstant.MSG_PROGRAM_SOURCE_NOT_NULL;
        }

        //屏幕分辨率宽高
        Integer screenWidth = programDataMap.get("screenWidth") == null ? 0 : (Integer) programDataMap.get("screenWidth");
        Integer screenHeight = programDataMap.get("screenHeight") == null ? 0 : (Integer) programDataMap.get("screenHeight");

        //画布宽高
        Integer canvasWidth = programDataMap.get("canvasWidth") == null ? 0 : (Integer) programDataMap.get("canvasWidth");
        Integer canvasHeight = programDataMap.get("canvasHeight") == null ? 0 : (Integer) programDataMap.get("canvasHeight");
        JSONObject jsonObject = JSONObject.parseObject(json);
        jsonObject.put("canvasWidth", canvasWidth);
        jsonObject.put("canvasHeight", canvasHeight);
        jsonObject.put("screenWidth", screenWidth);
        jsonObject.put("screenHeight", screenHeight);
        json = jsonObject.toJSONString();

        //清空节目原有场景和素材
        sceneMapper.batchDeleteByProgramVersion(program.getProgramVersion());
        sceneSourceMapper.batchDeleteByProgramVersion(program.getProgramVersion());

        //批量插入场景，素材
        if (CollectionUtils.isNotEmpty(scenes)) {
            sceneMapper.batchInsert(scenes);
        }
        if (CollectionUtils.isNotEmpty(sceneSourceList)) {
            sceneSourceMapper.batchInsert(sceneSourceList);
        }


        JsonData jsonData = null;
        //节目json
        if (null == program.getJsonId()) {
            jsonData = new JsonData();
            jsonData.setAddTime(now);
            jsonData.setJsonContent(json);
            jsonData.setType(JsonDataTypeEnum.PROGRAM_JSON.getType());
            jsonDataMapper.insert(jsonData);
            program.setJsonId(jsonData.getJsonId());
        } else {
            jsonData = jsonDataMapper.selectByPrimaryKey(program.getJsonId());
            if (jsonData != null) {
                jsonData.setJsonContent(json);
                jsonData.setUpdateTime(now);
                jsonDataMapper.updateByPrimaryKey(jsonData);
            } else {
                jsonData = new JsonData();
                jsonData.setAddTime(now);
                jsonData.setJsonContent(json);
                jsonData.setType(JsonDataTypeEnum.PROGRAM_JSON.getType());
                jsonDataMapper.insert(jsonData);
                program.setJsonId(jsonData.getJsonId());
                //删除旧的jsonData数据
                jsonDataMapper.deleteByPrimaryKey(program.getJsonId());
            }
        }

        program.setUpdateTime(now);
        program.setProgramSize(programSize);
        program.setWide(screenWidth);
        program.setHeight(screenHeight);
        program.setSceneNum(scenes.size());
        program.setProgramDuration(programDuration);
        program.setOverdueTime(DateUtils.getPreMonth(now));
        program.setAddStatus(ProgramConstant.ADD_STATUS_APPROVAL_SET_LAUNCH);
        program.setProgramTemplateId(programTemplateId);
        programMapper.updateByPrimaryKey(program);
        operationLogService.saveSuccessOperationLog(
                ("编辑节目成功，节目名称为：" + program.getProgramName() + "，节目版本号为：" + program.getProgramVersion()),
                ModuleConstant.MODULE_PROGRAM, loginUser.getUser(), loginUser.getIp());
        cpProgramLogService.saveSuccessOperationLog(program.getProgramVersion(), ProgramLogConstant.OPT_EDIT_PROGRAM, loginUser.getUser());
        return "";
    }

    /**
     * 刷新终端缓存
     *
     * @param programVersion 节目版本号
     * @param companyCode    商家编码
     * @param serialNo       批次号，唯一且有意义。（如：时间戳）
     */
    private void flushCache(String programVersion, String companyCode, Long serialNo) {
        //刷新缓存
        List<String> versionList = new ArrayList<>();
        versionList.add(programVersion);
        List<String> codeList = cpDeviceInfoService.listDeviceCodeByCompanyCodeAndProgramVersion(companyCode,
                versionList);
        Program program = programMapper.selectByProgramVersion(programVersion);
        if (null != program) {
            cpDeviceInfoService.flushDeviceStateCache(companyCode, DeviceSettingType.PROGRAM_UPDATE, codeList, serialNo);
        }
    }

    /**
     * 组装终端html模板节目json
     *
     * @param programId 节目主键
     * @return CpProgramVO
     */
    private String createJsonForZip(Long programId, HttpServletRequest request) throws IOException, ServiceException {
        Program program = programMapper.selectByPrimaryKey(programId);
        if (null == program) {
            return null;
        }
        CpProgramDTO cpProgramDTO = this.selectByPrimaryKeyDTO(programId, request);
        String json = cpProgramDTO.getJson();
        CpProgramZipBO cpProgramVO = new CpProgramZipBO();
        cpProgramVO.setXcVersion(program.getProgramVersion());
        cpProgramVO.setJsonName(program.getProgramName());
        cpProgramVO.setDuration(program.getProgramDuration());
        List<CpSceneSourceZipBO> source = sceneSourceMapper.listByProgramVersion(program.getProgramVersion());
        List<Scene> sceneList = sceneMapper.listByProgramVersion(program.getProgramVersion());
        //投放设置
        ProgramPutin programPutin = programPutinMapper.selectByProgramVersion(program.getProgramVersion());
        if (null != programPutin) {
            cpProgramVO.setPutinStartTime(programPutin.getPutinStartTime());
            cpProgramVO.setPutinEndTime(programPutin.getPutinEndTime());
        }
        if (CollectionUtils.isNotEmpty(source)) {
            File zip = ResourceUtils.getFile(ProgramZipConstant.ZIP_FOLDER);
            String sourceBasePath = zip.getAbsolutePath() + ProgramZipConstant.SLASH + ProgramZipConstant.SOURCE_FOLDER;
            String fileSaveUrl = ProgramZipConstant.PROTOCOL + ProgramZipConstant.HUAWEI_OSS_BUCKET_NAME
                    + "." + ProgramZipConstant.HUAWEI_OSS_ENDPOINT + ProgramZipConstant.SLASH;

            String sourceBaseUrl = ProgramZipConstant.HTML_JSON_URL_PREFIX;
            // key为文件名（md5值+'.'+文件后缀）, value为华为云存储全路径
            Map<String, String> sourceUrlMap = new HashMap<>(source.size());
            for (CpSceneSourceZipBO aSource : source) {
                //创建资源在html中的引用路径： ./source/35rr3432423423ewfw.png
                String sourceUrlInHtml = createSourceUrlInHtml(sourceUrlMap, aSource.getMd5(), aSource.getUrl(), sourceBaseUrl);
                //替换json中资源路径
                json = json.replace(aSource.getUrl(), sourceUrlInHtml);
            }
            //场景背景图
            for (Scene scene : sceneList) {
                //替换json中资源路径
                if (StringUtils.isNotBlank(scene.getBackground())) {
                    //创建资源在html中的引用路径： ./source/35rr3432423423ewfw.png
                    String sourceUrlInHtml = createSourceUrlInHtml(sourceUrlMap, String.valueOf(System.currentTimeMillis()), scene.getBackground(), sourceBaseUrl);
                    json = json.replace(scene.getBackground(), sourceUrlInHtml);
                }
            }
            //下载素材到source目录下
            for (Map.Entry<String, String> entry : sourceUrlMap.entrySet()) {
                String url = entry.getValue();
                String uri = url.substring(url.indexOf(fileSaveUrl) + fileSaveUrl.length());
                String huaweiUrlNew = fileSaveUrl + URLEncoder.encode(uri, ProgramZipConstant.CHARSET);
                FileUtils.downloadFileFromURL(huaweiUrlNew, (sourceBasePath + ProgramZipConstant.SLASH + entry.getKey()));
                logger.error("下载节目资源成功：" + url);
            }
        }
        return json;
    }

    /**
     * 压缩节目zip包
     *
     * @param programId 节目ID
     * @return zip包存储路径
     * @throws IOException
     */
    private String createProgramZip(Long programId, HttpServletRequest request) throws IOException, ServiceException {
        String huaWeiFilePath = "";
        //创建html需要的格式的数据
        String programJson = createJsonForZip(programId, request);
        if (StringUtils.isBlank(programJson)) {
            return huaWeiFilePath;
        }
        //创建临时文件用以创建节目zip
        File zip = ResourceUtils.getFile(ProgramZipConstant.ZIP_FOLDER);
        String basePath = zip.getAbsolutePath();
        String templatePath = basePath + ProgramZipConstant.SLASH + ProgramZipConstant.DEVICE_PLAY_TEMPLATE_HTML;
        String newHtmlFilePath = basePath + ProgramZipConstant.SLASH + ProgramZipConstant.DEVICE_PLAY_INDEX_HTML;
        //创建临时文件
        String activeProfile = springContextUtil.getActiveProfile();
        File newHtmlFile = HtmlUtils.createNewHtmlByTemplateForThreeScreen(templatePath, newHtmlFilePath, programJson, activeProfile);
        if (null != newHtmlFile) {
            //临时文件夹存储zip包
            File zipTempFolder = new File(ProgramZipConstant.ZIP_TEMP_FOLDER);
            zipTempFolder.mkdir();
            String zipTargetPath = zipTempFolder.getAbsolutePath() + ProgramZipConstant.SLASH + programId + ProgramZipConstant.ZIP_SUFFIX;
            ZipCompressUtils zipCompressUtils = new ZipCompressUtils(zipTargetPath);
            zipCompressUtils.compress(basePath);
            logger.error("压缩成zip包成功：" + zipTargetPath);
            File zipFile = new File(zipTargetPath);
            huaWeiFilePath = HuaWeiCloudUtils.uploadFile2HuaweiCloud(zipFile, ProgramZipConstant.HUA_WEI_ZIP_FOLDER);
            if (StringUtils.isNotBlank(huaWeiFilePath)) {
                FileUtils.delAllFile(basePath + ProgramZipConstant.SLASH + ProgramZipConstant.SOURCE_FOLDER);
                FileUtils.delAllFile(zipTempFolder.getAbsolutePath());
                newHtmlFile.delete();
            }
        }
        return huaWeiFilePath;
    }

    /**
     * 根据md5,url组装html页面需要的资源文件名称
     *
     * @param md5 文件md5
     * @param url 资源存储在华为云的url
     * @return 新的文件名称
     */
    private String createSourceNameByUrl(String md5, String url) {
        String name = "";
        if (StringUtils.isNotBlank(md5) && StringUtils.isNotBlank(url)) {
            //获取文件后缀名
            int index = url.lastIndexOf(ProgramZipConstant.SLASH);
            String sourceName = url.substring(index + 1);
            if (StringUtils.isNotBlank(sourceName)) {
                name = md5 + sourceName.substring(sourceName.indexOf("."));
            }
        }
        return name;
    }

    /**
     * 创建html中资源的url
     *
     * @param sourceUrlMap        资源存储在华为云的路径map
     * @param md5                 文件md5
     * @param url                 资源存储在华为云的url
     * @param sourceBaseUrlInHtml html中资源的基础url
     * @return html中资源的url
     */
    private String createSourceUrlInHtml(Map<String, String> sourceUrlMap, String md5, String url, String sourceBaseUrlInHtml) {
        String sourceNameByUrl = createSourceNameByUrl(md5, url);
        if (!sourceUrlMap.containsKey(sourceNameByUrl)) {
            sourceUrlMap.put(sourceNameByUrl, url);
        }
        return sourceBaseUrlInHtml + sourceNameByUrl;
    }
}
